package com.sojson.util.mq.rabbit.immit.interfaces.impl;

import java.io.IOException;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitOperations;

import com.sojson.util.StringUtil;
import com.sojson.util.mq.rabbit.immit.interfaces.IRabbitConsumeUtil;
import com.sojson.util.mq.rabbit.immit.interfaces.IRabbitMsgUtil;
import com.sojson.util.mq.rabbit.immit.interfaces.IRabbitQueueUtil;

/**
 * Rabbit基础工具类</BR>
 * 基础的消息生产消费模式,直接将消息发送至某队列,并从某队列直接获取</BR>
 * 
 * @author liu
 * @date 2022-06-16
 */
public class RabbitBaseUtilImpl implements IRabbitQueueUtil, IRabbitMsgUtil, IRabbitConsumeUtil {

    private static RabbitBaseUtilImpl instances;

    /**
     * 开始生产消息
     * 
     * @param channel
     * @param routingKey    消息的Key(如果没有交换机,此时是将消息发布到的队列名)
     * @param body          内容
     * @return
     * @throws IOException
     */
    public void basicPublish(RabbitOperations channel, String routingKey, String body) throws IOException {
        basicPublish(channel, routingKey, false, body);
    }

    /**
     * 开始生产消息
     * 
     * @param channel
     * @param routingKey    消息的Key(如果没有交换机,此时是将消息发布到的队列名)
     * @param mandatory     是否开启消息故障检测模式
     *                      它只会让RabbitMq向你通知失败,而不会通知成功
     *                      设为true的时候,使用前要先调用channel.addReturnListener(new ReturnCallback(){})
     *                      channel.addReturnListener(new ReturnListener(){})为异步返回
     *                      如果为true,消息不能路由到指定的队列时,会触发channel.BasicReturn事件
     *                      如果为false,则broker会直接将消息丢弃
     * @param body          内容
     * @return
     * @throws IOException
     */
    public void basicPublish(RabbitOperations channel, String routingKey, boolean mandatory, String body)
        throws IOException {
        basicPublish(channel, routingKey, mandatory, false, getBasicProperties(), body);
    }

    /**
     * 开始生产消息
     * 
     * @param channel
     * @param routingKey    消息的Key(如果没有交换机,此时是将消息发布到的队列名)
     * @param mandatory     是否开启消息故障检测模式
     *                      它只会让RabbitMq向你通知失败,而不会通知成功
     *                      设为true的时候,使用前要先调用channel.addReturnListener(new ReturnCallback(){})
     *                      channel.addReturnListener(new ReturnListener(){})为异步返回
     *                      如果为true,消息不能路由到指定的队列时,会触发channel.BasicReturn事件
     *                      如果为false,则broker会直接将消息丢弃
     * @param immediate     RabbitMQ 3.0版本开始去掉了对immediate参数的支持
     *                      原因是因为immediate参数会影响镜像队列的性能,增加了代码的复杂度,建议采用TTL和DLX的方法替代
     *                      当immediate参数设置为true时,如果交换器再将消息路由到一个队列中,否则将消息返回给生产者
     *                      immediate参数告诉服务器,如果该消息关联的队列上有消费者,则立刻投递
     *                      如果所有匹配的队列上都没有消费者,则直接将消息返还给生产者,不用将消息存于的队列而等待消费者
     * @param props         其它的一些属性(内容格式,类型,请求头等)
     * @param body          内容
     * @return
     * @throws IOException
     */
    public void basicPublish(RabbitOperations channel, String routingKey, boolean mandatory, boolean immediate,
        MessageProperties props, String body) throws IOException {
        // convertAndSend队列接收消息的时候是无序的
        // convertSendAndReceive队列接收消息的时候是有序的
        Message msg = new Message(body.getBytes(), props);
        channel.send(getRoutingKeyName(routingKey), msg);
    }

    /**
     * 判断一个对象是否不是空(空对象)
     * 
     * @param obj
     * @return
     */
    private static boolean isBlankObject(Object obj) {
        return StringUtil.isBlankObject(obj);
    }

    public static RabbitBaseUtilImpl getInstances() {
        if (isBlankObject(instances)) {
            instances = new RabbitBaseUtilImpl();
        }
        return instances;
    }

}